/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                       (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License (version 2) as published
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with 
    Amrita ITEWS if not then it can be viewed here: 
    http://itews.amrita.ac.in/license.html.

    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/

    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews

    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa 
    Vidyapeetham as part of the project titled,"Intelligent & Interactive 
    Telematics using Emerging Wireless Technologies for Transport Systems 
    (ITEWS)" sponsored by Technology Information Forecasting and Assessment 
    Council (TIFAC), India.
***/
package gpsnodelocator;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Hashtable;
import org.knopflerfish.service.log.LogRef;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;

public class GPSNodeLocator 
{
	private static final String CURRENT_NODE ="currentNode";
	private static final String CURRENT_NODE_SEQNO ="currentNodeSeqNo";
	private static final String NO_NODE ="nonode";
	private static final String NO_ROUTE ="noroute";
	private EventAdmin eventAdmin;
	private  LogRef log;
	private GPSNodeLocatorConfig config;
	private DBConnectionPool dbConnectionPool ;
	private String lastNodeCode = "";
	private String currentRouteCode = "";
	private boolean routeChanged = true;
	private String persistentDataQuery = "SELECT current_node_code, current_route_code, current_node_seq_no FROM persistent_data LIMIT 1";
	private String nodesQuery = "SELECT node_code FROM nodes WHERE MBRContains(node_area, GeomFromText(?))"; 
	private String routeScheduleNodeAssocQuery = "SELECT node_seq_no FROM route_schedule_node_assoc WHERE node_code=? AND route_schedule_code=?";
	private String currNodeQuery = "SELECT id FROM curr_node_gps";
	private String currNodeInsert = "INSERT INTO curr_node_gps VALUES(default, ?,default)";
	private String currNodeUpdate = "UPDATE curr_node_gps SET node_code=?";
	private int currentNodeSeqNo = 0;
	
	public GPSNodeLocator(EventAdmin eventAdmin)
	{
		try
		{
			this.eventAdmin = eventAdmin;
			log = new LogRef(Activator.bc);
			config = GPSNodeLocatorConfig.getConfig();
			dbConnectionPool = DBConnectionPool.getInstance(config.getJdbcConnectionURI());
			Connection conn = null;
			PreparedStatement preparedStmt1 = null;
			PreparedStatement preparedStmt2 = null;
			PreparedStatement preparedStmt3 = null;
			ResultSet rs1 = null;
			ResultSet rs2 = null;
			try 
	    	{
				conn = dbConnectionPool.getConnection();
				preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, currNodeQuery);
				rs1= preparedStmt1.executeQuery();
		    	if (rs1.next() == false)
		    	{
		    		preparedStmt2 = dbConnectionPool.getPreparedStmt(conn, currNodeInsert);
		    		preparedStmt2.setString(1, NO_NODE);
		    		preparedStmt2.executeUpdate();
			    	preparedStmt2.close();
			    	preparedStmt2 = null;
		    	}
		    	rs1.close();
		    	rs1 = null;
		    	preparedStmt1.close();
		    	preparedStmt1 = null;
		    	
		    	preparedStmt3 = dbConnectionPool.getPreparedStmt(conn, persistentDataQuery);
				rs2 = preparedStmt3.executeQuery();
				if (rs2.next() == true)
				{
					lastNodeCode =  rs2.getString(1).trim();
					currentRouteCode = rs2.getString(2).trim();
					currentNodeSeqNo = rs2.getInt(3);
				}
				rs2.close();
				rs2 = null;
				preparedStmt3.close();
				preparedStmt3 = null;
	    	}
	    	catch (Exception e) 
			{
	    		e.printStackTrace();
	    		throw e;
			}
			finally
			{
				if (rs1 != null)
	    		{
	    			try {rs1.close();} catch (SQLException e) {} 
	    			rs1 = null;
	    		}
	    		if (preparedStmt1 != null)
	    		{
	    			try {preparedStmt1.close();} catch (SQLException e) {} 
	    			preparedStmt1 = null;
	    		}
	    		if (preparedStmt2 != null)
	    		{
	    			try {preparedStmt2.close();} catch (SQLException e) {} 
	    			preparedStmt2 = null;
	    		}
	    		if (preparedStmt3 != null)
	    		{
	    			try {preparedStmt3.close();} catch (SQLException e) {} 
	    			preparedStmt3 = null;
	    		}
				if (conn != null)
				{
					try {conn.close();} catch (SQLException e) {}
					conn = null;
				}
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	protected synchronized void locateAndPublishCurrentNode(String currentLatitude, String currentLongitude)
	{
		if (currentRouteCode == null || currentRouteCode.equals("") || currentRouteCode.equals(NO_ROUTE))
		{
			log.info("Cannot handle node events since current route is either not set or is noroute. ");
			return;
		}
		Connection conn = null;
		PreparedStatement preparedStmt1 = null;
		PreparedStatement preparedStmt2 = null;
		ResultSet rs1 = null;
		ResultSet rs2 = null;
		String currentNodeCode = null;
		try 
		{
			StringBuffer currentLongLatPointBuffer = new StringBuffer("Point(");
			currentLongLatPointBuffer.append(currentLongitude).append(" ").append(currentLatitude).append(")");
			conn = dbConnectionPool.getConnection();
			preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, nodesQuery);
			preparedStmt1.setString(1, currentLongLatPointBuffer.toString());
			rs1 = preparedStmt1.executeQuery();
			outerwhile: while(true)
			{
				if (rs1.next() == true)
				{
					currentNodeCode = rs1.getString(1);
					log.debug("GPS Node Locator: Node reached "+currentNodeCode+" : "+currentLongLatPointBuffer.toString());
					preparedStmt2 = dbConnectionPool.getPreparedStmt(conn, routeScheduleNodeAssocQuery);
					preparedStmt2.setString(1, currentNodeCode);
					preparedStmt2.setString(2, currentRouteCode);
					rs2 = preparedStmt2.executeQuery();
					if (rs2.next() == false)
					{
						log.debug("GPSNodeLocator: Node not in route schedule: "+currentNodeCode);
						currentNodeCode = NO_NODE;
					}
					else
					{
						currentNodeSeqNo = rs2.getInt(1);
						break outerwhile;
					}
					rs2.close();
					rs2 = null;
					preparedStmt2.close();
					preparedStmt2 = null;
				}
				else
				{
					currentNodeCode = NO_NODE;
					log.debug("GPSNodeLocator: Not a node: " +currentLongLatPointBuffer.toString());
					break outerwhile;
				}
			}
			rs1.close();
			rs1 = null;
			preparedStmt1.close();
			preparedStmt1 = null;
			if (currentNodeCode != null) 
			{
				if (routeChanged == true || lastNodeCode == null || !lastNodeCode.equals(currentNodeCode))
				{
					notifyLocatedNode(currentNodeCode, currentNodeSeqNo);
					routeChanged = false;
				}
			}
		}
 		catch (Exception e) 
 		{
 			log.error("Error in during node location" + e);
 			e.printStackTrace();
 		}
 		finally
 		{
    		if (rs1 != null)
    		{
    			try {rs1.close();} catch (SQLException e) {} 
    			rs1 = null;
    		}
    		if (rs2 != null)
    		{
    			try {rs2.close();} catch (SQLException e) {} 
    			rs2 = null;
    		}
    		if (preparedStmt1 != null)
    		{
    			try {preparedStmt1.close();} catch (SQLException e) {} 
    			preparedStmt1 = null;
    		}
    		if (preparedStmt2 != null)
    		{
    			try {preparedStmt2.close();} catch (SQLException e) {} 
    			preparedStmt2 = null;
    		}
			if (conn != null)
			{
				try {conn.close();} catch (SQLException e) {}
				conn = null;
			}
 		}
	}
	
	private void notifyLocatedNode(String currentNodeCode, int currentNodeSeqNo) 
	{
		Hashtable nodeInfo = new Hashtable();
		nodeInfo.put(CURRENT_NODE, currentNodeCode.trim());
		nodeInfo.put(CURRENT_NODE_SEQNO, currentNodeSeqNo);
		Event event = new Event(CURRENT_NODE, nodeInfo);
		this.eventAdmin.postEvent(event);
		
		Connection conn = null;
		PreparedStatement preparedStmt1 = null;
		try 
    	{
			conn = dbConnectionPool.getConnection();
    		preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, currNodeUpdate);
    		preparedStmt1.setString(1, currentNodeCode);
    		preparedStmt1.executeUpdate();
	    	preparedStmt1.close();
	    	preparedStmt1 = null;
    	}
    	catch (Exception e) 
		{
			log.error("GPS NodeLocator: Error in fetching and playing Media from queue");
    		e.printStackTrace();
		}
		finally
		{
    		if (preparedStmt1 != null)
    		{
    			try {preparedStmt1.close();} catch (SQLException e) {} 
    			preparedStmt1 = null;
    		}
			if (conn != null)
			{
				try {conn.close();} catch (SQLException e) {}
				conn = null;
			}
		}
		log.info("Published Current Node from GPSNodeLocator bundle" +currentNodeCode);
	}

	protected synchronized void setCurrentRouteCode(String newRouteCode)
	{
		if (!this.currentRouteCode.equals(newRouteCode))
			routeChanged = true;
		this.currentRouteCode = newRouteCode;
		if (this.currentRouteCode.equals(NO_ROUTE))
		{
			currentNodeSeqNo = -1;
		}
		else
		{
			currentNodeSeqNo = 0;
		}
		lastNodeCode = "";
	}
	
	protected synchronized void setLastNode(String lastNodeCode)
	{
		this.lastNodeCode = lastNodeCode;
	}
}
